package jwtUtil

import (
	"context"
	"dev-assistant-backend/utility/codemsg"
	"dev-assistant-backend/utility/configs"
	"time"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/golang-jwt/jwt/v5"
)

type Payload struct {
	UserId string `json:"user_id"`
	Name   string `json:"name"`
	Email  string `json:"email"`
	Phone  string `json:"phone"`
	Role   int    `json:"role"`
	Status int    `json:"status"`
}

func NewPayload(
	userId string,
	name string,
	email string,
	phone string,
	role int,
	status int,
) *Payload {
	return &Payload{
		UserId: userId,
		Name:   name,
		Email:  email,
		Phone:  phone,
		Role:   role,
		Status: status,
	}
}

func GeneratToken(ctx context.Context, payload *Payload) (string, error) {
	jwtConfig := configs.GetToken(ctx)
	if jwtConfig == nil {
		g.Log().Line(true).Error(ctx, "jwt config is not set")
		return "", codemsg.InternalServerErr
	}

	token := jwt.NewWithClaims(
		jwt.SigningMethodHS256,
		jwt.MapClaims{
			"exp":     time.Now().Add(time.Hour * 24 * 7).Unix(),
			"user_id": payload.UserId,
			"name":    payload.Name,
			"email":   payload.Email,
			"phone":   payload.Phone,
			"role":    payload.Role,
			"status":  payload.Status,
			"iat":     time.Now().Unix(),
			"iss":     "dev-assistant-backend",
		})

	return token.SignedString([]byte(jwtConfig.Secret))
}

func ParseToken(ctx context.Context, jwtStr string) (*Payload, error) {
	jwtConfig := configs.GetToken(ctx)
	if jwtConfig == nil {
		g.Log().Line(true).Error(ctx, "jwt config is not set")
		return nil, codemsg.InternalServerErr
	}

	token, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, codemsg.InvalidJwtErr
		}
		return []byte(jwtConfig.Secret), nil
	})

	if err != nil {
		g.Log().Line(true).Warning(ctx, "jwt parse error", "error", err)
		return nil, codemsg.InvalidJwtErr
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		g.Log().Line(true).Error(ctx, "jwt claims is not a map")
		return nil, codemsg.InvalidJwtErr
	}

	if claims["exp"].(float64) < float64(time.Now().Unix()) {
		g.Log().Line(true).Warning(ctx, "jwt expired")
		return nil, codemsg.InvalidJwtErr
	}
	if claims["iss"] != "dev-assistant-backend" {
		g.Log().Line(true).Warning(ctx, "jwt issuer is not dev-assistant-backend")
		return nil, codemsg.InvalidJwtErr
	}
	if claims["iat"].(float64) > float64(time.Now().Unix()) {
		g.Log().Line(true).Warning(ctx, "jwt issued in the future")
		return nil, codemsg.InvalidJwtErr
	}

	return NewPayload(
		claims["user_id"].(string),
		claims["name"].(string),
		claims["email"].(string),
		claims["phone"].(string),
		claims["role"].(int),
		claims["status"].(int),
	), nil
}
